package com.kexilo.config.autoconfigure;

import com.kexilo.config.properties.ConfigProperties;
import com.kexilo.config.service.ConfigService;
import com.kexilo.config.service.ConfigServiceImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.*;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import javax.sql.DataSource;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 配置插件自动配置类
 * 根据条件自动装配配置相关组件
 * 
 * @author Kexilo
 */
@AutoConfiguration
@ConditionalOnClass({ConfigService.class})
@ConditionalOnProperty(value = "kexilo.plugin.config.enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(ConfigProperties.class)
@EnableAsync
public class ConfigAutoConfiguration {
    
    private static final Logger log = LoggerFactory.getLogger(ConfigAutoConfiguration.class);
    
    /**
     * 配置JdbcTemplate Bean
     * 复用admin模块的DataSource配置
     */
    @Bean
    @ConditionalOnClass({DataSource.class, JdbcTemplate.class})
    @ConditionalOnMissingBean(JdbcTemplate.class)
    @ConditionalOnProperty(value = "kexilo.plugin.config.data-source-type", havingValue = "DATABASE", matchIfMissing = true)
    public JdbcTemplate configJdbcTemplate(DataSource dataSource) {
        log.info("配置配置插件JdbcTemplate，复用admin模块DataSource");
        return new JdbcTemplate(dataSource);
    }
    
    /**
     * 配置本地缓存管理器
     * 当没有其他缓存管理器时使用
     */
    @Bean("configCacheManager")
    @ConditionalOnMissingBean(name = "configCacheManager")
    @ConditionalOnProperty(value = "kexilo.plugin.config.cache-strategy", havingValue = "LOCAL", matchIfMissing = true)
    public CacheManager configCacheManager(ConfigProperties properties) {
        ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
        cacheManager.setCacheNames(java.util.Collections.singletonList(properties.getCache().getCacheName()));
        cacheManager.setAllowNullValues(properties.getCache().isCacheNullValues());
        
        log.info("配置配置本地缓存管理器，缓存名称: {}", properties.getCache().getCacheName());
        return cacheManager;
    }
    
    /**
     * 配置ConfigService Bean
     */
    @Bean
    @ConditionalOnMissingBean(ConfigService.class)
    public ConfigService configService(ConfigProperties properties,
                                     @Autowired(required = false) JdbcTemplate jdbcTemplate,
                                     @Autowired(required = false) CacheManager cacheManager) {
        
        log.info("配置配置服务，缓存策略: {}, 数据源类型: {}", 
                properties.getCacheStrategy(), properties.getDataSourceType());
        return new ConfigServiceImpl(properties, jdbcTemplate, cacheManager);
    }
    
    /**
     * 配置异步任务执行器（专用于配置操作）
     */
    @Bean("configTaskExecutor")
    @ConditionalOnProperty(value = "kexilo.plugin.config.async-enabled", havingValue = "true", matchIfMissing = true)
    @ConditionalOnMissingBean(name = "configTaskExecutor")
    public ThreadPoolTaskExecutor configTaskExecutor(ConfigProperties properties) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(1);
        executor.setMaxPoolSize(3);
        executor.setQueueCapacity(50);
        executor.setThreadNamePrefix("kexilo-config-");
        executor.setKeepAliveSeconds(60);
        executor.setAllowCoreThreadTimeOut(false);
        
        // 拒绝策略：调用者运行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        
        // 等待任务完成后关闭线程池
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        
        executor.initialize();
        
        log.info("配置配置异步执行器，核心线程数: 1, 最大线程数: 3");
        return executor;
    }
    
    /**
     * 配置缓存预热配置
     */
    @Bean
    @ConditionalOnProperty(value = "kexilo.plugin.config.warmup-enabled", havingValue = "true", matchIfMissing = true)
    public ApplicationRunner configCacheWarmupRunner(ConfigService configService, ConfigProperties properties) {
        return args -> {
            if (properties.isWarmupEnabled()) {
                log.info("开始配置缓存预热...");
                try {
                    configService.warmupCache(properties.getWarmupKeys());
                    log.info("配置缓存预热完成");
                } catch (Exception e) {
                    log.warn("配置缓存预热失败", e);
                }
            }
        };
    }
    
    /**
     * 定时刷新配置缓存
     */
    @Bean
    @ConditionalOnProperty(value = "kexilo.plugin.config.refresh-strategy", havingValue = "SCHEDULED")
    public ConfigScheduledRefresher configScheduledRefresher(ConfigService configService, ConfigProperties properties) {
        log.info("配置定时刷新配置缓存，间隔: {}", properties.getRefresh().getScheduledInterval());
        return new ConfigScheduledRefresher(configService, properties);
    }
    
    /**
     * 配置插件健康检查配置
     */
    @Bean
    @ConditionalOnClass(name = "org.springframework.boot.actuate.health.HealthIndicator")
    @ConditionalOnProperty(value = "kexilo.plugin.config.health.enabled", havingValue = "true")
    public ConfigHealthIndicator configHealthIndicator(ConfigService configService, ConfigProperties properties) {
        log.info("配置配置插件健康检查");
        return new ConfigHealthIndicator(configService, properties);
    }
    
    /**
     * 配置插件性能监控配置
     */
    @Bean
    @ConditionalOnProperty(value = "kexilo.plugin.config.monitor.enabled", havingValue = "true", matchIfMissing = true)
    public ConfigMonitorService configMonitorService(ConfigService configService, ConfigProperties properties) {
        log.info("配置配置插件性能监控");
        return new ConfigMonitorService(configService, properties);
    }
    
    /**
     * 配置刷新端点（集成Spring Boot Actuator）
     */
    @Bean
    @ConditionalOnClass(name = "org.springframework.boot.actuate.endpoint.annotation.Endpoint")
    @ConditionalOnProperty(value = "kexilo.plugin.config.refresh-endpoint.enabled", havingValue = "true")
    public ConfigRefreshEndpoint configRefreshEndpoint(ConfigService configService) {
        log.info("配置配置刷新端点");
        return new ConfigRefreshEndpoint(configService);
    }
    
    /**
     * 定时刷新器
     */
    public static class ConfigScheduledRefresher {
        
        private final ConfigService configService;
        private final ConfigProperties properties;
        private final Logger log = LoggerFactory.getLogger(ConfigScheduledRefresher.class);
        
        public ConfigScheduledRefresher(ConfigService configService, ConfigProperties properties) {
            this.configService = configService;
            this.properties = properties;
        }
        
        @Scheduled(fixedDelayString = "#{@configProperties.refresh.scheduledInterval.toMillis()}")
        public void refreshConfigs() {
            try {
                log.debug("开始定时刷新配置缓存");
                configService.refreshCache(null); // 刷新所有配置
                log.debug("定时刷新配置缓存完成");
            } catch (Exception e) {
                log.error("定时刷新配置缓存失败", e);
                
                if (properties.getRefresh().isRetryOnFailure()) {
                    // 重试逻辑
                    for (int i = 1; i <= properties.getRefresh().getMaxRetries(); i++) {
                        try {
                            Thread.sleep(properties.getRefresh().getRetryInterval().toMillis());
                            configService.refreshCache(null);
                            log.info("配置缓存刷新重试成功，重试次数: {}", i);
                            break;
                        } catch (Exception retryE) {
                            log.warn("配置缓存刷新重试失败，重试次数: {}", i, retryE);
                        }
                    }
                }
            }
        }
    }
    
    /**
     * 配置插件健康检查指示器
     */
    public static class ConfigHealthIndicator {
        
        private final ConfigService configService;
        private final ConfigProperties properties;
        
        public ConfigHealthIndicator(ConfigService configService, ConfigProperties properties) {
            this.configService = configService;
            this.properties = properties;
        }
        
        public String checkHealth() {
            try {
                if (!properties.isEnabled()) {
                    return "DISABLED";
                }
                
                boolean available = configService.isAvailable();
                return available ? "UP" : "DOWN";
                
            } catch (Exception e) {
                return "DOWN: " + e.getMessage();
            }
        }
        
        public String getServiceInfo() {
            return String.format("ConfigService[%s] - CacheStrategy: %s, DataSourceType: %s, RefreshStrategy: %s", 
                    configService.getServiceName(),
                    properties.getCacheStrategy(),
                    properties.getDataSourceType(),
                    properties.getRefreshStrategy());
        }
        
        public java.util.Map<String, Object> getDetailedHealth() {
            java.util.Map<String, Object> details = new java.util.HashMap<>();
            
            details.put("enabled", properties.isEnabled());
            details.put("cacheStrategy", properties.getCacheStrategy());
            details.put("dataSourceType", properties.getDataSourceType());
            details.put("refreshStrategy", properties.getRefreshStrategy());
            details.put("asyncEnabled", properties.isAsyncEnabled());
            details.put("warmupEnabled", properties.isWarmupEnabled());
            details.put("changeListenerEnabled", properties.isChangeListenerEnabled());
            details.put("available", configService.isAvailable());
            
            if (properties.getMonitor().isCacheHitRateEnabled()) {
                details.put("cacheStats", configService.getCacheStats());
            }
            
            return details;
        }
    }
    
    /**
     * 配置插件监控服务
     */
    public static class ConfigMonitorService {
        
        private final ConfigService configService;
        private final ConfigProperties properties;
        private final Logger log = LoggerFactory.getLogger(ConfigMonitorService.class);
        
        public ConfigMonitorService(ConfigService configService, ConfigProperties properties) {
            this.configService = configService;
            this.properties = properties;
        }
        
        /**
         * 监控缓存命中率
         */
        @Scheduled(fixedRate = 60000) // 每分钟检查一次
        public void monitorCacheHitRate() {
            if (!properties.getMonitor().isCacheHitRateEnabled()) {
                return;
            }
            
            try {
                var stats = configService.getCacheStats();
                Double hitRate = (Double) stats.get("hitRate");
                
                if (properties.getMonitor().isAlertEnabled() && 
                    hitRate < properties.getMonitor().getHitRateAlertThreshold()) {
                    log.warn("配置缓存命中率过低告警: {}, 阈值: {}", 
                            hitRate, properties.getMonitor().getHitRateAlertThreshold());
                }
                
                log.debug("配置缓存统计: {}", stats);
                
            } catch (Exception e) {
                log.error("监控缓存命中率失败", e);
            }
        }
        
        /**
         * 获取监控报告
         */
        public java.util.Map<String, Object> getMonitorReport() {
            java.util.Map<String, Object> report = new java.util.HashMap<>();
            
            try {
                report.put("serviceAvailable", configService.isAvailable());
                report.put("cacheStats", configService.getCacheStats());
                report.put("monitorConfig", java.util.Map.of(
                    "enabled", properties.getMonitor().isEnabled(),
                    "slowQueryThreshold", properties.getMonitor().getSlowQueryThreshold(),
                    "cacheHitRateEnabled", properties.getMonitor().isCacheHitRateEnabled(),
                    "alertEnabled", properties.getMonitor().isAlertEnabled()
                ));
                
            } catch (Exception e) {
                log.error("获取监控报告失败", e);
                report.put("error", e.getMessage());
            }
            
            return report;
        }
    }
    
    /**
     * 配置刷新端点
     */
    public static class ConfigRefreshEndpoint {
        
        private final ConfigService configService;
        private final Logger log = LoggerFactory.getLogger(ConfigRefreshEndpoint.class);
        
        public ConfigRefreshEndpoint(ConfigService configService) {
            this.configService = configService;
        }
        
        public java.util.Map<String, Object> refresh(String configKey) {
            java.util.Map<String, Object> result = new java.util.HashMap<>();
            
            try {
                configService.refreshCache(configKey);
                result.put("success", true);
                result.put("message", configKey != null ? 
                          "配置已刷新: " + configKey : "所有配置已刷新");
                result.put("timestamp", System.currentTimeMillis());
                
                log.info("通过端点刷新配置: {}", configKey != null ? configKey : "ALL");
                
            } catch (Exception e) {
                log.error("刷新配置失败: {}", configKey, e);
                result.put("success", false);
                result.put("message", "刷新失败: " + e.getMessage());
                result.put("timestamp", System.currentTimeMillis());
            }
            
            return result;
        }
        
        public java.util.Map<String, Object> getCacheStats() {
            try {
                return configService.getCacheStats();
            } catch (Exception e) {
                log.error("获取缓存统计失败", e);
                return java.util.Map.of("error", e.getMessage());
            }
        }
    }
}
